#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <stddef.h>
#include <sys/stat.h>
#include <stdbool.h>

#include <pthread.h>
#include <time.h>
#include <dirent.h>

typedef int unit; // 基本数据块单元，存64位数据
#define PMAX 47 // p最大值
#define PATHLEN_MAX 1024 // 文件路径最大长度
#define THREAD_MAX 8 // 线程最大数量

typedef struct {
    int p;
    unit data[];
} block;

// 每个存储文件的文件头
typedef struct {
    int p;
    size_t size, block_num, last_block_size; // 数据总长度,分治块数,最后一块长度
} file_header; 

typedef struct {
    block item;
    struct cache_queue *next;
} cache_queue;

void write_data(const char *path, const char *file_name , const int p);

void repair_data(const int p, int idx0, int idx1);

void read_data(const char *file_name, const char *path, const int p);

/**
 * Retrieves the header information of a file.
 *
 * @param path The path to the file.
 * @param p The value of p.
 * @param header The pointer to the file_header structure to store the header information.
 *
 * @throws None
 */
void get_file_header(const char* path, const int p, file_header *header)
{
    #ifdef DEBUG
    printf("Enter get_file_header\n");
    #endif
    header->p = p;
    FILE *fp = fopen(path, "rb");
    if (fseek(fp, 0, SEEK_END))
    {
        puts("File does not exist");
        exit(0);
    }
    header->size = ftell(fp);
    fclose(fp);
    size_t block_size = sizeof(unit) * p * (p - 1);
    header->last_block_size = header->size % block_size;
    header->block_num = header->size / block_size;
    #ifdef DEBUG
    printf("get_file_header completed ! \np = %d, size = %ld, block_num = %ld, last_block_size = %ld\n", p, header->size, header->block_num, header->last_block_size);
    #endif
}

void write_file_header(FILE *fp, file_header *header){
    fwrite(header, sizeof(file_header), 1, fp);
}

//读取文件头
void read_file_header(FILE *fp, file_header *header){
    fread(header, sizeof(file_header), 1, fp);
}

// 磁盘 到 bl->data
void read_block(block *bl, FILE *fp[])
{
    int num = bl->p;
    for(int i = 0; i < num; i++)
        fread(bl->data + i * (bl->p - 1), sizeof(unit), bl->p - 1, fp[i]);
}
/**
 * Prints the usage instructions for the evenodd program.
 *
 * @return void
 */
void explain()
{
    printf("Usage: \n");
    printf("./evenodd write <p> <input_path> <file_name> -- write data\n");
    printf("./evenodd read <p> <file_name> <output_path> -- read data\n");
    printf("./evenodd repair <p> <idx0> [<idx1>]         -- repair data\n");
}

// 原文件 到 bl->data
void read_in(FILE *fp, block *bl)
{
    size_t num = bl->p * (bl->p - 1);
    size_t ok = fread(bl->data, 1, sizeof(unit) * num, fp);
    #ifdef DEBUG
    // printf("ok = %ld\n", ok);
    #endif
    memset((void *)bl->data + ok, 0, sizeof(unit) * (bl->p - 1) * (bl->p + 2) - ok); 
}

// bl->data 到 磁盘
void write_down(block *bl, FILE *fp[])
{
    int num = bl->p + 2;
    for (int i = 0; i < num; ++i)
        fwrite(bl->data + i * (bl->p - 1), sizeof(unit), bl->p - 1, fp[i]);
}

// bl->data 到 原始文件
void read_out(FILE *fp, block *bl, int num)
{
    if (!num) fwrite(bl->data, 1, sizeof(unit) * bl->p * (bl->p - 1), fp);
    else fwrite(bl->data, 1, num, fp);
}

//写入最后两列校验列
void write_down_last2(block *bl, FILE *fp[])
{
    int num = bl->p + 2;
    for (int i = bl->p; i < num; ++i)
        fwrite(bl->data + i * (bl->p - 1), sizeof(unit), bl->p - 1, fp[i]);
}

//block->data 存储时先列后行，便于写入
/*
    1   2
    3   4
    5   6
    校验列1
    校验列2

    那么5的c就是0，r就是2
    6的c就是1，r就是2；

*/
int pos(int c, int r, int p)
{
    return r * (p - 1) + c;
}

unit* pos_extend(unit *extend, block*block, int c, int r, int p)
{
    if(c == p-1)
        return &extend[r];
    else
        return &(block -> data[r * (p - 1) + c]);
}


// 第一校验列
void produce_data_c1(block *bl)
{
    for (int i = 0; i < bl->p - 1; ++i)
    {
        bl->data[pos(i, bl->p, bl->p)] = 0;
    }
    for (int i = 0; i < bl->p - 1; ++i)
    {
        for (int j = 0; j < bl->p; ++j)
        {
            bl->data[pos(i, bl->p, bl->p)] ^= bl->data[pos(i, j, bl->p)];
        }
    }
}

// 第二校验列
void produce_data_c2(block *bl)
{
    unit S = 0;
    for (int i = 0; i < bl->p - 1; ++i)
    {
        bl->data[pos(i, bl->p + 1, bl->p)] = 0;
    }
    for (int i = 0; i < bl->p - 1; ++i)
    {
        S ^= bl->data[pos(i, bl->p - 1 - i, bl->p)];
    }
    for (int i = 0; i < bl->p - 1; ++i)
    {
        for (int j = 0; j < bl->p; ++j)
        {
            if (j != i + 1) 
                bl->data[pos(i, bl->p + 1, bl->p)] ^= bl->data[pos((bl->p + i - j) % bl->p, j, bl->p)];
        }
        bl->data[pos(i, bl->p + 1, bl->p)] ^= S;
    }
}

void produce_data(block *bl)
{
    produce_data_c1(bl);
    produce_data_c2(bl);
}

void write_data(const char *path, const char *file_name , const int p)
{
    #ifdef DEBUG
    printf("Enter write_data\n");
    #endif
    FILE *in = fopen(path, "rb");
    if (in == NULL)
    {
        puts("File does not exist");
        exit(0);
    }
    FILE *out[PMAX + 1];
    file_header *header = (file_header *)malloc(sizeof(file_header));
    // 获得文件头
    get_file_header(path, p, header);
    // 初始化存储文件
    for (int i = 0; i < p + 2; ++i)
    {
        char save_path[PATHLEN_MAX];
        sprintf(save_path, "disk_%d", i);
        mkdir(save_path, S_IRWXU | S_IRWXG | S_IRWXO);
        sprintf(save_path, "disk_%d/%s", i, file_name);
        out[i] = fopen(save_path, "wb");
        write_file_header(out[i], header);
    }
    int block_sum = header->last_block_size == 0 ? header->block_num : header->block_num + 1; 

    #ifdef DEBUG
    FILE *check = fopen("check.dat", "wb");
    // write_file_header(check, header);
    #endif

    // 写入数据
    for (int i = 0; i < block_sum; ++i)
    {
        block *bl = (block *)malloc(sizeof(block) + sizeof(unit) * (p + 2) * (p - 1));
        bl->p = p;
        read_in(in, bl);
        produce_data(bl);
        write_down(bl, out);
        #ifdef DEBUG
        if (!(header->last_block_size && i == block_sum - 1))
            read_out(check, bl, 0);
        else if (i == block_sum - 1)
            read_out(check, bl, header->last_block_size);
        #endif
        free(bl);
    }
    fclose(in);
    for (int i = 0; i < p + 2; ++i)
        fclose(out[i]);
    free(header);
    #ifdef DEBUG
    fclose(check);    
    #endif
}

//修复两个校验盘
int repair_2bad_case1(const int p, int idx0, int idx1)
{
    file_header *header = (file_header*)malloc(sizeof(file_header));

    char save_path1[PATHLEN_MAX];//第一个损坏盘的目录
    char save_path2[PATHLEN_MAX];//第二个损坏盘的目录
    char filename[256];
    sprintf(save_path1, "disk_%d", idx0);
    sprintf(save_path2, "disk_%d", idx1);
    mkdir(save_path1, S_IRWXU | S_IRWXG | S_IRWXO);
    mkdir(save_path2, S_IRWXU | S_IRWXG | S_IRWXO);
    
    DIR *dir;
    struct dirent *entry;
    dir = opendir("disk_0");

    while ((entry = readdir(dir)) != NULL)
    {
        sprintf(filename, "%s", entry->d_name);
        
        if (strcmp(filename, ".") == 0 || strcmp(filename, "..") == 0)
            continue;
        
        sprintf(save_path1, "disk_%d/%s", idx0, filename);
        sprintf(save_path2, "disk_%d/%s", idx1, filename);
        
        #ifdef DEBUG
        printf("save_path1:%s\n", save_path1);
        printf("save_path2:%s\n", save_path2);
        #endif

        FILE *in[PMAX + 1];
        in[p] = fopen(save_path1, "wb"); in[p+1] = fopen(save_path2, "wb");
        for(int i = 0; i < p; i++)
        {
            char fpath[PATHLEN_MAX];
            sprintf(fpath, "disk_%d/%s",i, filename);
            in[i] = fopen(fpath, "rb");
        }

        //读取磁盘0的文件头
        for(int i = 0; i < p; i++)
            read_file_header(in[i], header);
        
        #ifdef DEBUG
        printf("header->p = %d\n", header->p);
        #endif

        int block_sum = header->last_block_size == 0 ? header->block_num : header->block_num + 1;

        for(int i = p; i < p + 2; i++)
            write_file_header(in[i], header);

        for(int i = 0; i < block_sum; i++)
        {
            block *bl = (block *)malloc(sizeof(block) + sizeof(unit) * (p + 2) * (p - 1));
            bl->p = p;
            read_block(bl, in);
            produce_data(bl);
            write_down_last2(bl, in);
            free(bl);
        }
        
        for(int i = 0; i < p + 2; i++)
            fclose(in[i]);
        
    }
    free(header);
    closedir(dir);
}

//修复两个原始数据盘
void repair_2bad_case2(const int p, int idx0, int idx1)
{
    file_header *header = (file_header*)malloc(sizeof(file_header));

    char save_path1[PATHLEN_MAX];//第一个损坏盘的目录
    char save_path2[PATHLEN_MAX];//第二个损坏盘的目录
    char filename[256];
    sprintf(save_path1, "disk_%d", idx0);
    sprintf(save_path2, "disk_%d", idx1);
    mkdir(save_path1, S_IRWXU | S_IRWXG | S_IRWXO);
    mkdir(save_path2, S_IRWXU | S_IRWXG | S_IRWXO);

    char data_c1[PATHLEN_MAX];//第一校验列的磁盘，用于获得文件标识符
    sprintf(data_c1, "disk_%d", p);
    
    DIR *dir;
    struct dirent *entry;
    dir = opendir(data_c1);

    while((entry = readdir(dir)) != NULL)   //每一次循环对应一个文件表示符的文件
    {
        sprintf(filename, "%s", entry->d_name);
        
        if (strcmp(filename, ".") == 0 || strcmp(filename, "..") == 0)
            continue;

        sprintf(save_path1, "disk_%d/%s", idx0, filename);
        sprintf(save_path2, "disk_%d/%s", idx1, filename);

        #ifdef DEBUG
        printf("save_path1:%s\n", save_path1);
        printf("save_path2:%s\n", save_path2);
        #endif

        FILE *in[PMAX + 1];
        in[idx0] = fopen(save_path1, "wb"); in[idx1] = fopen(save_path2, "wb");
        for(int i = 0; i < p + 2; i++)
        {
            if(i != idx0 && i != idx1)
            {
                char fpath[PATHLEN_MAX];
                sprintf(fpath, "disk_%d/%s",i, filename);
                in[i] = fopen(fpath, "rb");
                read_file_header(in[i], header);
            }
        }
        

        int block_sum = header->last_block_size == 0 ? 
                        header->block_num : header->block_num + 1;

        write_file_header(in[idx0], header);
        write_file_header(in[idx1], header);

        //每次循环对应一个block
        for(int i = 0; i < block_sum; i++)
        {
            block *bl = (block *)malloc(sizeof(block) + sizeof(unit) * (p + 2) * (p - 1));
            bl->p = p;
            for(int i = 0; i < p + 2; i++)
            {
                if(i != idx0 && i != idx1)
                    fread(bl->data + i * (p - 1), sizeof(unit), p - 1, in[i]);
            }
            unit S = 0;
            unit extend[PMAX + 2];
            for(int i = 0 ; i < p + 2; i ++)
                extend[i] = 0;

            for(int i = 0; i <= p - 2; i++)
            {
                S ^= *pos_extend(extend, bl, i, p, p);
                S ^= *pos_extend(extend, bl, i, p+1, p);
            }

            unit S0[PMAX];
            unit S1[PMAX];

            for(int i = 0; i <= p - 1; i++)
            {
                S0[i] = 0;
                S1[i] = S;
                S1[i] ^= *pos_extend(extend, bl, i, p+1, p);
            }

            for(int u = 0; u <= p - 1; u++)
            {
                for(int l = 0; l <= p; l++)
                {
                    if(l != idx0 && l != idx1)
                        S0[u] ^= *pos_extend(extend, bl, u, l, p);
                }
            }
            
            for(int u = 0; u <= p - 1; u++)
            {
                for(int l = 0; l <= p - 1; l++)
                {
                    if(l != idx0 && l != idx1)
                        S1[u] ^= *pos_extend(extend, bl, (u-l + p) % p, l, p);
                }
            }

            int step = (-(idx1 - idx0) - 1 + p) % p;
            while(1)
            {
                unit temp = *pos_extend(extend, bl, 
                            ((idx1 - idx0) + step + p) % p, idx0, p);
                *pos_extend(extend, bl, step, idx1, p) = 
                    S1[(step + idx1 + p) % p] ^ temp;
                *pos_extend(extend, bl, step, idx0, p) = 
                *pos_extend(extend, bl, step, idx1, p) ^ S0[step];
                step = (step - (idx1 - idx0) + p) % p;
                if(step == p -1)
                    break;
            }

            fwrite(bl->data + idx0 * (bl->p - 1), sizeof(unit), bl->p - 1, in[idx0]);
            fwrite(bl->data + idx1 * (bl->p - 1), sizeof(unit), bl->p - 1, in[idx1]);

            free(bl);
        }

        for(int i = 0; i < p + 2; i++)
            fclose(in[i]);
        
    }
    free(header);
    closedir(dir);
}

//修复第一个校验盘和一个数据盘
void repair_2bad_case3(const int p, const int idx0, const int idx1)
{
    file_header *header = (file_header*)malloc(sizeof(file_header));

    char save_path1[PATHLEN_MAX];//第一个损坏盘的目录
    char save_path2[PATHLEN_MAX];//第二个损坏盘的目录
    char filename[256];
    sprintf(save_path1, "disk_%d", idx0);
    sprintf(save_path2, "disk_%d", idx1);
    mkdir(save_path1, S_IRWXU | S_IRWXG | S_IRWXO);
    mkdir(save_path2, S_IRWXU | S_IRWXG | S_IRWXO);

    char data_c1[PATHLEN_MAX];//第二校验列的磁盘，用于获得文件标识符
    sprintf(data_c1, "disk_%d", p + 1);
    
    DIR *dir;
    struct dirent *entry;
    dir = opendir(data_c1);

    while((entry = readdir(dir)) != NULL)   //每一次循环对应一个文件表示符的文件
    {
        sprintf(filename, "%s", entry->d_name);
        
        if (strcmp(filename, ".") == 0 || strcmp(filename, "..") == 0)
            continue;

        sprintf(save_path1, "disk_%d/%s", idx0, filename);
        sprintf(save_path2, "disk_%d/%s", idx1, filename);

        #ifdef DEBUG
        printf("save_path1:%s\n", save_path1);
        printf("save_path2:%s\n", save_path2);
        #endif

        FILE *in[PMAX + 1];
        in[idx0] = fopen(save_path1, "wb"); in[idx1] = fopen(save_path2, "wb");
        for(int i = 0; i < p + 2; i++)
        {
            if(i != idx0 && i != idx1)
            {
                char fpath[PATHLEN_MAX];
                sprintf(fpath, "disk_%d/%s",i, filename);
                in[i] = fopen(fpath, "rb");
                read_file_header(in[i], header);
            }
        }
        

        int block_sum = header->last_block_size == 0 ? 
                        header->block_num : header->block_num + 1;

        write_file_header(in[idx0], header);
        write_file_header(in[idx1], header);

        //每次循环对应一个block
        for(int i = 0; i < block_sum; i++)
        {
            block *bl = (block *)malloc(sizeof(block) + sizeof(unit) * (p + 2) * (p - 1));
            bl->p = p;
            for(int i = 0; i < p + 2; i++)
            {
                if(i != idx0 && i != idx1)
                    fread(bl->data + i * (p - 1), sizeof(unit), p - 1, in[i]);
            }
            
            unit S;
            unit extend[PMAX + 2];
            for(int i = 0 ; i < p + 2; i ++)
                extend[i] = 0;

            S = *pos_extend(extend, bl, (idx0 - 1 + p) % p, p+1, p);

            for(int l = 0; l <= p - 1; l++)
                S ^= *pos_extend(extend, bl, (idx0 - l - 1 + p) % p, l, p);

            for(int k = 0; k <= p - 2; k++)
            {
                *pos_extend(extend, bl, k, idx0, p) = 
                    S ^ *pos_extend(extend, bl, (idx0 + k + p) % p, p + 1, p);
                for(int l = 0; l <= p - 1; l++){
                    if(l != idx0)
                    {
                    *pos_extend(extend, bl, k, idx0, p) ^=
                        *pos_extend(extend, bl, (k + idx0 - l + p) % p, l, p); 
                    }
                }
            }

            produce_data_c1(bl);
            fwrite(bl->data + idx0 * (bl->p - 1), sizeof(unit), bl->p - 1, in[idx0]);
            fwrite(bl->data + idx1 * (bl->p - 1), sizeof(unit), bl->p - 1, in[idx1]);
            free(bl);
        }
        for(int i = 0; i <= p + 1; i++)
            fclose(in[i]);
    }
    free(header);
    closedir(dir);

}

//修复第二个校验盘和一个数据盘
void repair_2bad_case4(const int p, const int idx0, const int idx1)
{
    #ifdef DEBUG
    printf("%s", "Catch case 4 \n");
    #endif
    file_header *header = (file_header*)malloc(sizeof(file_header));

    char save_path1[PATHLEN_MAX];//第一个损坏盘的目录
    char save_path2[PATHLEN_MAX];//第二个损坏盘的目录
    char filename[256];
    sprintf(save_path1, "disk_%d", idx0);
    sprintf(save_path2, "disk_%d", idx1);
    mkdir(save_path1, S_IRWXU | S_IRWXG | S_IRWXO);
    mkdir(save_path2, S_IRWXU | S_IRWXG | S_IRWXO);

    char data_c1[PATHLEN_MAX];//第一校验列的磁盘，用于获得文件标识符
    sprintf(data_c1, "disk_%d", p);
    
    DIR *dir;
    struct dirent *entry;
    dir = opendir(data_c1);

    while((entry = readdir(dir)) != NULL)   //每一次循环对应一个文件表示符的文件
    {
        sprintf(filename, "%s", entry->d_name);
        
        if (strcmp(filename, ".") == 0 || strcmp(filename, "..") == 0)
            continue;

        sprintf(save_path1, "disk_%d/%s", idx0, filename);
        sprintf(save_path2, "disk_%d/%s", idx1, filename);

        #ifdef DEBUG
        printf("save_path1:%s\n", save_path1);
        printf("save_path2:%s\n", save_path2);
        #endif

        FILE *in[PMAX + 1];
        in[idx0] = fopen(save_path1, "wb"); in[idx1] = fopen(save_path2, "wb");
        for(int i = 0; i < p + 2; i++)
        {
            if(i != idx0 && i != idx1)
            {
                char fpath[PATHLEN_MAX];
                sprintf(fpath, "disk_%d/%s",i, filename);
                in[i] = fopen(fpath, "rb");
                read_file_header(in[i], header);
            }
        }
        

        int block_sum = header->last_block_size == 0 ? 
                        header->block_num : header->block_num + 1;

        write_file_header(in[idx0], header);
        write_file_header(in[idx1], header);

        //每次循环对应一个block
        for(int i = 0; i < block_sum; i++)
        {
            block *bl = (block *)malloc(sizeof(block) + sizeof(unit) * (p + 2) * (p - 1));
            bl->p = p;
            for(int i = 0; i < p + 2; i++)
            {
                if(i != idx0 && i != idx1)
                    fread(bl->data + i * (p - 1), sizeof(unit), p - 1, in[i]);
            }
            
            unit S;
            unit extend[PMAX + 2];
            for(int i = 0 ; i < p + 2; i ++)
                extend[i] = 0;

            S = *pos_extend(extend, bl, (idx0 - 1 + p) % p, p+1, p);

            for(int k = 0; k <= p -2 ; k++)
            {
                *pos_extend(extend, bl, k, idx0, p) = 0;
                for(int l = 0; l <= p; l++)
                {
                    if(l != idx0)
                    {*pos_extend(extend, bl, k, idx0, p) ^= 
                        *pos_extend(extend, bl, k, l, p);}                     
                }
            }
            produce_data_c2(bl);
            fwrite(bl->data + idx0 * (bl->p - 1), sizeof(unit), bl->p - 1, in[idx0]);
            fwrite(bl->data + idx1 * (bl->p - 1), sizeof(unit), bl->p - 1, in[idx1]);
            free(bl);
        }

        for(int i = 0; i <= p + 1; i++)
            fclose(in[i]);
    }
    free(header);
    closedir(dir);
}

//修复一个原始数据盘
void repair_2bad_case5(const int p, const int idx0, const int idx1)
{
    #ifdef DEBUG
    printf("%s", "Catch case 4 \n");
    #endif
    file_header *header = (file_header*)malloc(sizeof(file_header));

    char save_path1[PATHLEN_MAX];//第一个损坏盘的目录
    char save_path2[PATHLEN_MAX];//第二个损坏盘的目录
    char filename[256];
    sprintf(save_path1, "disk_%d", idx0);
    sprintf(save_path2, "disk_%d", idx1);
    mkdir(save_path1, S_IRWXU | S_IRWXG | S_IRWXO);
    mkdir(save_path2, S_IRWXU | S_IRWXG | S_IRWXO);

    char data_c1[PATHLEN_MAX];//第一校验列的磁盘，用于获得文件标识符
    sprintf(data_c1, "disk_%d", p);
    
    DIR *dir;
    struct dirent *entry;
    dir = opendir(data_c1);

    while((entry = readdir(dir)) != NULL)   //每一次循环对应一个文件表示符的文件
    {
        sprintf(filename, "%s", entry->d_name);
        
        if (strcmp(filename, ".") == 0 || strcmp(filename, "..") == 0)
            continue;

        sprintf(save_path1, "disk_%d/%s", idx0, filename);
        sprintf(save_path2, "disk_%d/%s", idx1, filename);

        #ifdef DEBUG
        printf("save_path1:%s\n", save_path1);
        printf("save_path2:%s\n", save_path2);
        #endif

        FILE *in[PMAX + 1];
        in[idx0] = fopen(save_path1, "wb"); in[idx1] = fopen(save_path2, "wb");
        for(int i = 0; i < p + 2; i++)
        {
            if(i != idx0 && i != idx1)
            {
                char fpath[PATHLEN_MAX];
                sprintf(fpath, "disk_%d/%s",i, filename);
                in[i] = fopen(fpath, "rb");
                read_file_header(in[i], header);
            }
        }
        

        int block_sum = header->last_block_size == 0 ? 
                        header->block_num : header->block_num + 1;

        write_file_header(in[idx0], header);
        write_file_header(in[idx1], header);

        //每次循环对应一个block
        for(int i = 0; i < block_sum; i++)
        {
            block *bl = (block *)malloc(sizeof(block) + sizeof(unit) * (p + 2) * (p - 1));
            bl->p = p;
            for(int i = 0; i < p + 2; i++)
            {
                if(i != idx0 && i != idx1)
                    fread(bl->data + i * (p - 1), sizeof(unit), p - 1, in[i]);
            }
            
            unit S;
            unit extend[PMAX + 2];
            for(int i = 0 ; i < p + 2; i ++)
                extend[i] = 0;

            S = *pos_extend(extend, bl, (idx0 - 1 + p) % p, p+1, p);

            for(int k = 0; k <= p -2 ; k++)
            {
                *pos_extend(extend, bl, k, idx0, p) = 0;
                for(int l = 0; l <= p; l++)
                {
                    if(l != idx0)
                    {*pos_extend(extend, bl, k, idx0, p) ^= 
                        *pos_extend(extend, bl, k, l, p);}                     
                }
            }
            fwrite(bl->data + idx0 * (bl->p - 1), sizeof(unit), bl->p - 1, in[idx0]);
            free(bl);
        }
        for(int i = 0; i <= p + 1; i++)
            fclose(in[i]);
    }
    free(header);
    closedir(dir);
}

void folderExists(const int p, int *idx0, int *idx1, const char* file_name)
{
    char foldername[PATHLEN_MAX];

    for(int i = 0; i <= p + 1; i++)
    {
        sprintf(foldername, "disk_%d/%s", i, file_name);
        FILE *file = fopen(foldername, "r");
        if(file == NULL)
        {
            *idx0 = i;
            break;
        }
        fclose(file);
    }

    for(int i = *idx0 + 1; i <= p + 1; i++)
    {
        sprintf(foldername, "disk_%d/%s", i, file_name);
        FILE *file = fopen(foldername, "r");
        if(!file)
        {
            *idx1 = i;
            break;
        }
        fclose(file);
    }
}

void read_data(const char *file_name, const char *path, const int p)
{
    #ifdef DEBUG
    printf("Enter read_data\n");
    #endif

    int idx0 = -1, idx1 = -1;
    folderExists(p, &idx0, &idx1, file_name);
    if(idx0 != -1)
        repair_data(p, idx0, idx1);

    FILE *in[PMAX + 1];
    FILE *out = fopen(path, "wb");
    file_header *header = (file_header*)malloc(sizeof(file_header));

    for(int i = 0; i < p; i++)
    {
        char fpath[PATHLEN_MAX];
        sprintf(fpath, "disk_%d/%s",i, file_name);
        in[i] = fopen(fpath, "rb");
    }

    for(int i = 0; i < p; ++i)
        read_file_header(in[i], header);
    
    #ifdef DEBUG
    printf("header->p = %d, header->size = %d, header->block_num = %d, header->last_block_size = %d\n", header->p, (int)header->size, (int)header->block_num, (int)header->last_block_size);
    #endif

    for (int i = 0; i < header->block_num; ++i)
    {
        block *bl = (block *)malloc(sizeof(block) + sizeof(unit) * (p + 2) * (p - 1));
        bl->p = p;
        read_block(bl, in);
        read_out(out, bl, 0);
        free(bl);
    }
    if (header->last_block_size != 0)
    {
        block *bl = (block *)malloc(sizeof(block) + sizeof(unit) * (p + 2) * (p - 1));
        bl->p = p;
        read_block(bl, in);
        read_out(out, bl, header->last_block_size);
        free(bl);
    }

    fclose(out);
    for(int i = 0; i < p; ++i)
        fclose(in[i]);
    free(header);
}

void repair_data(const int p, int idx0, int idx1)
{
    if (idx0 > idx1 && idx1 != -1)
    {
        int tmp = idx0;
        idx0 = idx1;
        idx1 = tmp;
    }
    if((idx0 == p && idx1 == p+1) ||  (idx1 == p && idx0 == p+1))
    //修复两个校验盘
        repair_2bad_case1(p, idx0, idx1);
    else if(idx0 < p && idx1 == -1)
    //修复一个原始数据盘
        repair_2bad_case5(p, idx0, p+1);
    else if(idx0 == p && idx1 == -1)
    //修复第一个校验盘
        repair_2bad_case1(p, p, p+1);
    else if(idx0 == p + 1 && idx1 == -1)
    //修复第二个校验盘
        repair_2bad_case1(p, p, p+1);
    else if(idx0 < p && idx1 < p)
    //修复两个原始数据盘
        repair_2bad_case2(p, idx0, idx1);
    else if(idx0 < p && idx1 == p)
    //修复第一个校验盘和一个数据盘
        repair_2bad_case3(p, idx0, idx1);
    else if(idx0 < p && idx1 == p+1)
    //修复第二个校验盘和一个数据盘
        repair_2bad_case4(p, idx0, idx1);
}

int main(int argc, char *argv[])
{
    #ifdef TEST
    clock_t start_time, end_time;
    start_time = clock();
    #endif
    char *cmd = argv[1];
    if (strcmp(cmd, "write") == 0)
    {
        write_data(argv[3], argv[4], atoi(argv[2]));
    }
    else if (strcmp(cmd, "read") == 0)
    {
        read_data(argv[3], argv[4], atoi(argv[2]));
    }
    else if (strcmp(cmd, "repair") == 0)
    {
        repair_data(atoi(argv[2]), atoi(argv[3]), argc == 5 ? atoi(argv[4]) : -1);
    }
    else {
        explain();
    }
    #ifdef TEST
    end_time = clock();
    printf("runtime = %lf\n", (double)(end_time - start_time) / CLOCKS_PER_SEC);
    #endif
    return 0;
}